/**
 * Stok3d - getting you stoked on Flash Player native 3D
 * Copyright (C) 2009  David Lenaerts
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author information:
 * David Lenaerts
 * url: http://www.derschmale.com
 * mail: david.lenaerts@gmail.com
 * 
 */

package
{
	import com.derschmale.stok3d.filters.EnvMapFaces;
	import com.derschmale.stok3d.filters.WaterShadingFilter;
	import com.derschmale.stok3d.utils.NormalMapUtil;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.filters.ShaderFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.TextField;
	
	/**
	 * Native FP10 3D Water shading demo: Ripple demo
	 * This also illustrates the use of NormalMapUtil
	 */
	
	[SWF(width="800", height="600", frameRate="30", backgroundColor="0x000000")]
	public class WaterExample2 extends Sprite
	{
		[Embed(source="/../exampleAssets/water/water_deep.png")]
		private var Texture : Class;
		
		[Embed(source="/../exampleAssets/envMapPhong/left.jpg")]
		private var ReflLeft : Class;
		
		[Embed(source="/../exampleAssets/envMapPhong/right.jpg")]
		private var ReflRight : Class;
		
		[Embed(source="/../exampleAssets/envMapPhong/front.jpg")]
		private var ReflFront : Class;
		
		[Embed(source="/../exampleAssets/envMapPhong/back.jpg")]
		private var ReflBack : Class;
		
		[Embed(source="/../exampleAssets/envMapPhong/top.jpg")]
		private var ReflTop : Class;
		
		[Embed(source="/../exampleAssets/envMapPhong/bottom.jpg")]
		private var ReflBottom : Class;
		
		[Embed(source="RippleKernel.pbj", mimeType="application/octet-stream")]
		private var RippleKernel : Class;
		
		private var _startX : Number;
		private var _startY : Number;
		
		private var _mouseDown : Boolean;
		
		private var _surface : Sprite;
		
		private var _filter : WaterShadingFilter;
		
		private var _lightRotation : Number = 0;
		
		// blowing up the ripples for the final normal map because it looks more interesting
		private var _heightMap1 : BitmapData = new BitmapData(128, 128, false, 0x000000);
		private var _heightMap2 : BitmapData = new BitmapData(128, 128, false, 0x000000);
		private var _normalMapTemp : BitmapData = new BitmapData(128, 128, false);
		private var _normalMap : BitmapData = new BitmapData(256, 256, false);
		private var _rippleFilter : ShaderFilter;
		
		private var _matrix : Matrix;
		
		public function WaterExample2()
		{
			_matrix = new Matrix(_normalMap.width/_heightMap1.width, 0, 0, _normalMap.height/_heightMap1.height);
			
			initView();
			initFilter();
			
			stage.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
			stage.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
			addEventListener(Event.ENTER_FRAME, handleEnterFrame);
		}
		
		private function initView() : void
		{
			// init background
			var bg : Bitmap = new Bitmap(new ReflBack().bitmapData);
			bg.width = stage.stageWidth;
			bg.height = stage.stageWidth;
			bg.smoothing = true;
			addChild(bg);
			
			var textField : TextField = new TextField();
			textField.textColor = 0xffffff;
			textField.multiline = true;
			textField.text = 	"Click & drag : Rotate surface\n" + 
								"f/g : Change surface relief";
			textField.width = textField.textWidth + 10;
			textField.height = textField.textHeight + 10;
			textField.x = stage.stageWidth-textField.width;
			addChild(textField);
			
			// init rotatable surface, make sure registration point is in center
			var bmp : Bitmap = new Texture();
			bmp.smoothing = true;
			bmp.x = -bmp.width*.5;
			bmp.y = -bmp.height*.5;
			_surface = new Sprite();
			_surface.addChild(bmp);
			
			// some ridiculously hight values all over the place
			_surface.z = 16000;
			
			// position and scale
			bmp.smoothing = true;
			_surface.addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
			_surface.x = stage.stageWidth*.5;
			_surface.y = stage.stageHeight*.5+3000;
			_surface.rotationX = 95;
			_surface.scaleY = 50;
			_surface.scaleX = 50;
			
			addChild(_surface);
		}
		
		private function handleMouseMove(event : MouseEvent) : void
		{
			var targetX : int = (event.target.mouseX+128)/_matrix.a;
			var targetY : int = (event.target.mouseY+128)/_matrix.d;
			var rect : Rectangle = new Rectangle(targetX-2, targetY-2, 4, 4);
			_heightMap1.lock();
			_heightMap1.fillRect(rect, 0xff0000);
			_heightMap1.unlock();
		}
		
		private function handleKeyUp(event : KeyboardEvent) : void
		{
			switch(event.keyCode) {
				case 71:
					_filter.normalMapStrength += 0.03;
					if (_filter.normalMapStrength > 0.5) _filter.normalMapStrength = 0.5;
					_surface.filters = [_filter];
					break;
				case 70:
					_filter.normalMapStrength -= 0.03;
					if (_filter.normalMapStrength < 0.0) _filter.normalMapStrength = 0.0;
					_surface.filters = [_filter];
					break;
			}
		}
		
		/**
		 * Create the EnvMapFilter object and assign it to the surface
		 */
		private function initFilter() : void
		{
			var faces : Array = [];
			faces[EnvMapFaces.LEFT] = new ReflLeft().bitmapData;
			faces[EnvMapFaces.RIGHT] = new ReflRight().bitmapData;
			faces[EnvMapFaces.TOP] = new ReflTop().bitmapData;
			faces[EnvMapFaces.BOTTOM] = new ReflBottom().bitmapData;
			faces[EnvMapFaces.FRONT] = new ReflFront().bitmapData;
			faces[EnvMapFaces.BACK] = new ReflBack().bitmapData; 
			
			_filter = new WaterShadingFilter(faces, 20, 3, 1, _normalMap, 0.1);
			_filter.ambientColor = 0x000010;
			_filter.specularColor = 0xffff99;
			_filter.update(_surface);
			_filter.lightPosition.z = 5000000;
			_filter.lightPosition.y = 300000;
			_filter.lightPosition.x = 1000000;
			_surface.filters = [ _filter ];
			
			var rippleShader : Shader = new Shader(new RippleKernel());
			_rippleFilter = new ShaderFilter(rippleShader);
			rippleShader.data.damping.value = [ 0.99 ];
		}
		
		/**
		 * Indicate rotation has started with the current mouse position as motion reference
		 */
		private function handleMouseDown(event : MouseEvent) : void
		{
			_mouseDown = true;
			_startX = mouseX;
			_startY = mouseY;
		}
		
		/**
		 * Indicate rotation has stopped
		 */
		private function handleMouseUp(event : MouseEvent) : void
		{
			_mouseDown = false;
		}
		
		/**
		 * Update filter and surface position
		 */
		private function handleEnterFrame(event : Event) : void
		{
			if (_mouseDown) {
		 		_surface.rotationY += (_startX-mouseX)*.01;
		 		_surface.rotationX += (_startY-mouseY)*.01;
		 	}
		 	updateNormalMap();
		 	_filter.update(_surface);
		 	_surface.filters = [ _filter ];
		}
		
		private function updateNormalMap() : void
		{
			_rippleFilter.shader.data.old.input = _heightMap2;
			_heightMap2.applyFilter(_heightMap1, _heightMap1.rect, new Point(), _rippleFilter);
			_normalMap.draw(_heightMap2, _matrix, null, null, null, true);
			NormalMapUtil.drawFromHeightMap(_normalMap, _normalMap, 1, true);
			
			var temp : BitmapData = _heightMap1;
			_heightMap1 = _heightMap2;
			_heightMap2 = temp;
			
		}
	}
}